#include <stdio.h>
#include <stdarg.h>

#include "SEGGER_RTT.h"

#include "drv_net.h"
#include "at_lexer.h"

#include "misc.h"

//private types

typedef struct _ENV_AT_LEXER {
    AT_LEXER_STATE nx_state;
    AT_LEXER_HANDLER pf_handler;
    uint16_t head;
    uint32_t stream_len;
    uint8_t pstream[AT_LEXER_MAX_STREAM_LEN];
}ENV_AT_LEXER;

//private variants
static ENV_AT_LEXER env_at_lexer = {
    .nx_state = AT_LEXER_STATE_HEAD,
    .pf_handler = NULL,
    .head = 0,
    .stream_len = 0
};


static AT_LEXER_STATE at_lexer_state_head(uint8_t data) {
    if (env_at_lexer.head == AT_DELIMITER) {
        return AT_LEXER_STATE_BODY;
    } else {
        return AT_LEXER_STATE_HEAD;
    }
}

static AT_LEXER_STATE at_lexer_state_send_ready(uint8_t data) {
    if (data == ' ') { //space
        env_at_lexer.pf_handler(TSTRING(uint8_t * , "> "));
    }
    return AT_LEXER_STATE_HEAD;
}

static AT_LEXER_STATE at_lexer_state_body(uint8_t data) {
    if (data == '\r') { //bc26
        return AT_LEXER_STATE_HEAD;
    } else if (data == '>') { //m5313 and a9600
        return AT_LEXER_STATE_SEND_READY;
    } else {
        env_at_lexer.pstream[0] = data;
        env_at_lexer.stream_len = 1;
        return AT_LEXER_STATE_TAIL;
    }
}

static AT_LEXER_STATE at_lexer_state_tail(uint8_t data);

static AT_LEXER_STATE(*at_lexer_state_table[AT_LEXER_STATE_MAX])(uint8_t) = {
    at_lexer_state_head,
    at_lexer_state_body,
    at_lexer_state_tail,
    at_lexer_state_send_ready,
    NULL
};

AT_LEXER_STATE at_lexer_state_tail(uint8_t data) {
    env_at_lexer.pstream[env_at_lexer.stream_len] = data;
    env_at_lexer.stream_len += 1;
    if (env_at_lexer.head == AT_DELIMITER) {
        if (env_at_lexer.stream_len <= sizeof(env_at_lexer.head)) { /** a new head */
            return AT_LEXER_STATE_BODY;
        } else {
            if (env_at_lexer.pf_handler != NULL) {
                env_at_lexer.stream_len -= sizeof(env_at_lexer.head);
                at_lexer_state_table[AT_LEXER_STATE_CUSTOM] = env_at_lexer.pf_handler(env_at_lexer.pstream, env_at_lexer.stream_len);
                env_at_lexer.stream_len = 0;
                if (at_lexer_state_table[AT_LEXER_STATE_CUSTOM]) {
                    return AT_LEXER_STATE_CUSTOM;
                } else {
                    return AT_LEXER_STATE_BODY;
                }
            } else {
                return AT_LEXER_STATE_BODY;
            }
        }
    } else if (env_at_lexer.stream_len >= AT_LEXER_MAX_STREAM_LEN) {
        NET_ERR("buffer overflow");
        return AT_LEXER_STATE_HEAD;
    } else {
        return AT_LEXER_STATE_TAIL;
    }
}


void at_lexer_execute(uint8_t data) {
#if defined(__ARM_BIG_ENDIAN) || defined(__BIG_ENDIAN)
    env_at_lexer.head >>= 8;
    env_at_lexer.head |= (data << 8);
#else
    env_at_lexer.head <<= 8;
    env_at_lexer.head |= data;
#endif
    if (env_at_lexer.nx_state < AT_LEXER_STATE_MAX) {
        env_at_lexer.nx_state = at_lexer_state_table[env_at_lexer.nx_state](data);
    } else {
        NET_ERR("invalid state");
    }
}


void at_lexer_handler_set(AT_LEXER_HANDLER pf_handler) {
    env_at_lexer.pf_handler = pf_handler;
}

AT_LEXER_HANDLER at_lexer_handler_get(void) {
    return env_at_lexer.pf_handler;
}


int at_match(BYTES *psrc, const char *ppattern, ...) {
    va_list args;
    void *pdst;
    union {
        uint32_t u32;
        int32_t s32;
        uint8_t *pdata;
    }t;
    uint32_t t_len;
    int32_t error = 0;
    uint8_t action;
    va_start(args, ppattern);
    while (*ppattern != '\0') {
        action = *ppattern;
        ppattern++;
        if (action == ' ') {
            //do nothing
        } else if (action == '+') {
            action = *ppattern;
            ppattern++;
            pdst = va_arg(args, void *);

            if (error == 0) {
                if (action == 'd') {
                    if (data_to_s32(&(psrc->pdata), &(psrc->len), &(t.s32)) == 0) {
                        if (pdst != NULL) {
                            *((int32_t *)pdst) = t.s32;
                        }
                    } else {
                        error = -1;
                    }
                } else if (action == 'u') {

                    if (data_to_u32(&(psrc->pdata), &(psrc->len), &(t.u32)) == 0) {
                        if (pdst != NULL) {
                            *((uint32_t *)pdst) = t.u32;
                        }
                    } else {
                        error = -1;
                    }
                } else if (action == 's') {
                    if (data_to_str(&(psrc->pdata), &(psrc->len), (char **)&(t.pdata), &t_len) == 0) {
                        if (pdst != NULL) {
                            ((BYTES *)pdst)->pdata = t.pdata;
                            ((BYTES *)pdst)->len = t_len;
                        }
                    } else {
                        error = -1;
                    }
                } else {
                    //fatal error,this error can be repaired before compiling
                    error = -2;
                    NET_ERR("invalid pattern-->%s", ppattern);
                    break;
                }
            }
        } else {
            if (error == 0) {
                if (data_skip(&(psrc->pdata), &(psrc->len), action)) {
                    error = -1;
                }
            }
        }
    }
    va_end(args);
    return error;
}

